#ifndef __LOGGER_H__
#define __LOGGER_H__
/*  日志器模块
        1.抽象日志器基类
        2.派生出不同的子类（同步日志器和异步日志器）
*/
#include "sink.hpp"
#include "level.hpp"
#include "util.hpp"
#include "message.hpp"
#include "formatter.hpp"
#include "looper.hpp"

#include <mutex>
#include <atomic>
#include <cstdarg>
#include <unordered_map>

namespace lcglog{
    class Logger{
        public:
            using ptr = std::shared_ptr<Logger>;//使用智能指针管理Logger对象

            Logger(const std::string& logger_name, 
            LogLevel::value level, 
            Formatter::ptr& formatter, 
            std::vector<LogSink::ptr>& sinks):
            _logger_name(logger_name),
            _limit_level(level),
            _formatter(formatter),
            _sinks(sinks.begin(), sinks.end()){}
            const std::string& name(){
                return _logger_name;
            }
            /*完成构造日志消息对象过程并进行格式化，得到格式化后的日志消息字符串，然后进行落地输出*/
            void debug(const std::string& file, size_t line, const std::string& fmt, ...){
                //通过传入的参数构造出一个日志消息对象，进行日志的格式化，最终落地
                //1.判断当前的日志是否达到了输出等级
                if(LogLevel::value::DEBUG < _limit_level){ return;}
                //2.对fmt格式化字符串和不定参进行字符串组织，得到的日志消息字符串
                char *res;
                va_list ap;
                va_start(ap, fmt);
                /*根据格式化字符串动态申请内存，并将结果装进res中*/
                int ret = vasprintf(&res, fmt.c_str(), ap);
                if(ret == -1){
                    std::cout << "vasprintf fail!\n";
                    return;
                }
                va_end(ap);
                serialize(LogLevel::value::DEBUG, file, line, res);
                free(res);//vasprintf()内部开辟了动态申请的空间，需要我们手动释放
            }
            void info(const std::string& file, size_t line, const std::string& fmt, ...){
                //通过传入的参数构造出一个日志消息对象，进行日志的格式化，最终落地
                //1.判断当前的日志是否达到了输出等级
                if(LogLevel::value::INFO < _limit_level){ return;}
                //2.对fmt格式化字符串和不定参进行字符串组织，得到的日志消息字符串
                char *res;
                va_list ap;
                va_start(ap, fmt);
                /*根据格式化字符串动态申请内存，并将结果装进res中*/
                int ret = vasprintf(&res, fmt.c_str(), ap);
                if(ret == -1){
                    std::cout << "vasprintf fail!\n";
                    return;
                }
                va_end(ap);
                serialize(LogLevel::value::INFO, file, line, res);
                free(res);
            }
            void warn(const std::string& file, size_t line, const std::string& fmt, ...){
                //通过传入的参数构造出一个日志消息对象，进行日志的格式化，最终落地
                //1.判断当前的日志是否达到了输出等级
                if(LogLevel::value::WARN < _limit_level){ return;}
                //2.对fmt格式化字符串和不定参进行字符串组织，得到的日志消息字符串
                char *res;
                va_list ap;
                va_start(ap, fmt);
                /*根据格式化字符串动态申请内存，并将结果装进res中*/
                int ret = vasprintf(&res, fmt.c_str(), ap);
                if(ret == -1){
                    std::cout << "vasprintf fail!\n";
                    return;
                }
                va_end(ap);
                serialize(LogLevel::value::WARN, file, line, res);
                free(res);
            }
            void error(const std::string& file, size_t line, const std::string& fmt, ...){
                //通过传入的参数构造出一个日志消息对象，进行日志的格式化，最终落地
                //1.判断当前的日志是否达到了输出等级
                if(LogLevel::value::ERROR < _limit_level){ return;}
                //2.对fmt格式化字符串和不定参进行字符串组织，得到的日志消息字符串
                char *res;
                va_list ap;
                va_start(ap, fmt);
                /*根据格式化字符串动态申请内存，并将结果装进res中*/
                int ret = vasprintf(&res, fmt.c_str(), ap);
                if(ret == -1){
                    std::cout << "vasprintf fail!\n";
                    return;
                }
                va_end(ap);
                serialize(LogLevel::value::ERROR, file, line, res);
                free(res);
            }
            void fatal(const std::string& file, size_t line, const std::string& fmt, ...){
                //通过传入的参数构造出一个日志消息对象，进行日志的格式化，最终落地
                //1.判断当前的日志是否达到了输出等级
                if(LogLevel::value::FATAL< _limit_level){ return;}
                //2.对fmt格式化字符串和不定参进行字符串组织，得到的日志消息字符串
                char *res;
                va_list ap;
                va_start(ap, fmt);
                /*根据格式化字符串动态申请内存，并将结果装进res中*/
                int ret = vasprintf(&res, fmt.c_str(), ap);
                if(ret == -1){
                    std::cout << "vasprintf fail!\n";
                    return;
                }
                va_end(ap);
                serialize(LogLevel::value::FATAL, file, line, res);
                free(res);
            }
        protected:
            /*抽象接口完成，实际的落地输出--不同的日志器会有不同的实际落地方式*/
            virtual void log(const char* data, size_t len) = 0;

            void serialize(LogLevel::value level, const std::string file, size_t line, char* res){
                //3.构造LogMsg对象
                LogMsg msg(level, line, file, _logger_name, res);
                //4.通过格式化工具对LogMsg进行格式化，得到格式化后的日志字符串
                std::stringstream ss;
                _formatter->format(ss, msg);
                //5.进行日志的落地
                log(ss.str().c_str(), ss.str().size());
            }
        protected:
            std::mutex _mutex;
            std::string _logger_name;
            std::atomic<LogLevel::value> _limit_level;
            Formatter::ptr _formatter;
            std::vector<LogSink::ptr> _sinks;
    };

    class SyncLogger : public Logger{
        public:
            SyncLogger(const std::string& logger_name, 
            LogLevel::value level, 
            Formatter::ptr& formatter, 
            std::vector<LogSink::ptr>& sinks):
            Logger(logger_name, level, formatter, sinks){}
        protected:
            /*同步日志器是将日志直接通过落地模块句柄进行日志落地*/
            void log(const char* data, size_t len) override{
                std::unique_lock<std::mutex> lock(_mutex);
                if(_sinks.empty()) return;

                for(auto& sink : _sinks){
                    sink->log(data, len);
                }
            }
    };
    class AsyncLogger : public Logger{
        public:
            AsyncLogger(const std::string& logger_name, 
            LogLevel::value level, 
            Formatter::ptr& formatter, 
            std::vector<LogSink::ptr>& sinks, AsyncType looper_type):
            Logger(logger_name, level, formatter, sinks),
            _looper(std::make_shared<AsyncLooper>(std::bind(&AsyncLogger::realLog, this, std::placeholders::_1), looper_type)){}
            //将数据写入缓冲区
            void log(const char* data, size_t len) override{
                _looper->push(data, len);
                }
            //设计一个实际落地函数（将缓冲区中的数据进行落地）
            void realLog(Buffer& buf){
                if(_sinks.empty()) return;
                for(auto& sink : _sinks){
                    sink->log(buf.begin(), buf.readAbleSize());
                }
            }
        private:
            AsyncLooper::ptr _looper;
    };
    /*  使用建造者模式来建造日志器，而不要让用户直接去构造日志器，简化用户的使用复杂度*/
    /*  1.抽象出一个日志器建造者类--完成日志器对象所需零部件的构建 & 日志器的构建
            1.设置日志器类型
            2.将不同类型日志器的创建放到同一个日志器建造者类中完成
    */
   enum class LoggerType{
    LOGGER_SYNC,
    LOGGER_ASYNC
   };

   class LoggerBuilder{
    public:
        LoggerBuilder():
        _limit_level(LogLevel::value::DEBUG),
        _logger_type(LoggerType::LOGGER_SYNC),
        _looper_type(AsyncType::ASYNC_SAFE){}

        void buildLoggerType(LoggerType type){
            _logger_type = type;
        }
        void buildLoggerName(const std::string& name){
            _logger_name = name;
        }
        void buildLoggerLevel(LogLevel::value level){
            _limit_level = level;
        }
        void buildLoggerFormatter(const std::string& pattern){
            _formatter = std::make_shared<Formatter>(pattern);
        }
        void buildEnableUnsafeAsync(){
            _looper_type = AsyncType::ASYNC_UNSAFE;
        }
        template<typename SinkType, typename ...Args>
            void buildSink(Args &&...args){
                //根据类型，创建一个日志落地器
                LogSink::ptr psink = std::make_shared<SinkType>(std::forward<Args>(args)...);
                _sinks.push_back(psink);
            }
        virtual Logger::ptr build() = 0;//建造日志器
    protected:
        AsyncType _looper_type;
        LoggerType _logger_type;
        std::string _logger_name;
        LogLevel::value _limit_level;//需要频繁访问
        Formatter::ptr _formatter;
        std::vector<LogSink::ptr> _sinks;
   };
    /*
        2.派生出具体的建造者类--局部日志器的建造者 & 全局日志器的建造者（后面添加了全局单例管理器之后，将日志器添加全局管理）
    */
   class LocalLoggerBuilder : public LoggerBuilder{
    public:
        Logger::ptr build() override{
            assert(_logger_name.empty() == false);//必须有日志器名称
            if(_formatter.get() == nullptr){
                _formatter = std::make_shared<Formatter>();
            }
            if(_sinks.empty()){
                buildSink<StdoutSink>();
            }
            if(_logger_type == LoggerType::LOGGER_ASYNC){
                return std::make_shared<AsyncLogger>(_logger_name, _limit_level, _formatter, _sinks, _looper_type);
            }
            return std::make_shared<SyncLogger>(_logger_name, _limit_level, _formatter, _sinks);
        }
   };
   //设计为单例模式-懒汉
   class LoggerManger{
    private:
        std::mutex _mutex;
        Logger::ptr _root_logger;//默认日志器
        std::unordered_map<std::string, Logger::ptr> _loggers;//日志器管理数组
    private:
        LoggerManger(){
            //这里builder必须管理的是本地的建造者，创建一个本地的局部变量，不能使用全局的建造者，否则程序会卡住
            std::unique_ptr<lcglog::LoggerBuilder> builder(new lcglog::LocalLoggerBuilder());
            builder->buildLoggerName("root");
            _root_logger = builder->build();

            _loggers.insert(std::make_pair("root", _root_logger));
        }
    public:
        static LoggerManger& getInstance(){
            /*在C++11之后，针对静态局部变量，编译器在编译的层面实现了线程安全*/
            /*当静态局部变量在没有构造完成之前，其他的线程进入就会阻塞*/
            static LoggerManger eton;
            return eton;
        } 
        void addLogger(Logger::ptr& logger){
            if(hasLogger(logger->name())){
                return;
            }
            std::unique_lock<std::mutex> lock(_mutex);//加锁
            _loggers.insert(std::make_pair(logger->name(), logger));
        }
        bool hasLogger(const std::string& name){

            std::unique_lock<std::mutex> lock(_mutex);//加锁
            auto it = _loggers.find(name);
            if(it == _loggers.end()){
                return false;
            }
            return true;
        }
        Logger::ptr getLogger(const std::string& name){

            std::unique_lock<std::mutex> lock(_mutex);//加锁
            auto it = _loggers.find(name);
            if(it == _loggers.end()){
                return Logger::ptr();
            }
            return it->second;
        }
        Logger::ptr rootLogger(){
            return _root_logger;
        }
   };
   /*设计一个全局日志器的建造者，在局部的基础上增加一个功能：自动添加日志器到单例对象中*/
    class GlobalLoggerBuilder : public LoggerBuilder{
    public:
        Logger::ptr build() override{
            assert(_logger_name.empty() == false);//必须有日志器名称
            if(_formatter.get() == nullptr){
                _formatter = std::make_shared<Formatter>();
            }
            if(_sinks.empty()){
                buildSink<StdoutSink>();
            }
            Logger::ptr logger;
            if(_logger_type == LoggerType::LOGGER_ASYNC){
                logger = std::make_shared<AsyncLogger>(_logger_name, _limit_level, _formatter, _sinks, _looper_type);
            }
            else{
                logger = std::make_shared<SyncLogger>(_logger_name, _limit_level, _formatter, _sinks);
            }
            LoggerManger::getInstance().addLogger(logger);
            return logger;
        }
   };
}

#endif